jQuery.fn.liveSearch = function (conf) {
	var config = jQuery.extend({
		classes: [],
		url: '/search-results.php?q=', 
		id: 'jquery-live-search', 
		duration: 400, 
		typeDelay: 200,
		loadingClass: 'loading',
		method: 'POST',
		requestParameter: 'search',
		onSlideUp: function () {},
		onSearchCompleted: function(data) { return data.html; }, 
		onSearchFailed: function(response) { 
			try {
				console.error(response);
			}
			catch(ex) { }
		},
		width: 'input',
		uptadePosition:	false,
		container: document.body
	}, conf);

	var liveSearch	= jQuery('#' + config.id);

	if (!liveSearch.length) {
		liveSearch = jQuery('<div id="' + config.id + '"></div>').appendTo(config.container).hide().slideUp(0);

		jQuery(document.body).click(function(event) {
			var clicked = jQuery(event.target);

			if (!(clicked.is('#' + config.id) || clicked.parents('#' + config.id).length || clicked.is('input'))) {
				liveSearch.slideUp(config.duration, function () {
					config.onSlideUp();
				});
			}
		});
	}

	for (i = 0; i < config.classes.length; i++) {
		var cssClass = config.classes[i];
		if (!liveSearch.hasClass(cssClass)) liveSearch.addClass(cssClass);
	}

	return this.each(function () {
		var input = jQuery(this).attr('autocomplete', 'off');
		var liveSearchPaddingBorderHoriz = parseInt(liveSearch.css('paddingLeft'), 10) + parseInt(liveSearch.css('paddingRight'), 10) + parseInt(liveSearch.css('borderLeftWidth'), 10) + parseInt(liveSearch.css('borderRightWidth'), 10);

		var repositionLiveSearch = function () {
			var tmpOffset = input.offset();
			var inputDim = {
				left: tmpOffset.left, 
				top: tmpOffset.top, 
				width: input.outerWidth(), 
				height: input.outerHeight()
			};

			inputDim.topPos = inputDim.top + inputDim.height;
			inputDim.totalWidth	= inputDim.width - liveSearchPaddingBorderHoriz;

			var liveSearchWidth = inputDim.width;
			if (config.width == 'auto') liveSearchWidth = 'auto';
			else if (config.width == 'input') { }
			else {
				try {
					liveSearchWidth = parseInt(config.width, inputDim.width);
				}
				catch (ex) {

				}
			}

			liveSearch.css({
				position: 'absolute', 
				left: inputDim.left + 'px', 
				top: inputDim.topPos + 'px',
				width: liveSearchWidth + 'px',
				'z-index': 1010
			});
		};

		var showLiveSearch = function () {
			repositionLiveSearch();

			$(window).unbind('resize', repositionLiveSearch);
			$(window).bind('resize', repositionLiveSearch);

			liveSearch.slideDown(config.duration);
		};

		var hideLiveSearch = function () {
			liveSearch.slideUp(config.duration, function () {
				config.onSlideUp();
			});
		};

		input.focus(function () {
			if (this.value !== '') {
				if (liveSearch.html() == '') {
					this.lastValue = '';
					input.keyup();
				}
				else {
					setTimeout(showLiveSearch, 1);
				}
			}
		}).keyup(function () {
			if (this.value != this.lastValue) {
				input.addClass(config.loadingClass);

				var q = this.value;

				if (this.timer) {
					clearTimeout(this.timer);
				}

				this.timer = setTimeout(function () {
					if (config.method == 'POST') {
						var param = { };
						param[config.requestParameter] = q;

						jQuery.post(config.url, param).done(function (data) {
							input.removeClass(config.loadingClass);

							if (data.length && q.length) {
								var e = {
									'data': data,
									'search': q,
									'html': data.toString()
								};

								var liveSearchHtml = config.onSearchCompleted(e);
								liveSearch.html(liveSearchHtml);
								showLiveSearch();
							}
							else {
								hideLiveSearch();
							}
						}).fail(function(response) {
							config.onSearchFailed(response);
						});
					}
					else {
						jQuery.get(config.url + q).done(function (data) {
							input.removeClass(config.loadingClass);

							if (data.length && q.length) {
								var e = {
									'data': data,
									'search': q,
									'html': data.toString()
								};

								var liveSearchHtml = config.onSearchCompleted(e);
								liveSearch.html(liveSearchHtml);
								showLiveSearch();
							}
							else {
								hideLiveSearch();
							}
						}).fail(function(response) {
							config.onSearchFailed(response);
						});
					}
				}, config.typeDelay);

				this.lastValue = this.value;
			}
		});
	});
};
